#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/io.h>
#include <linux/errno.h>
#include <linux/delay.h>
// 设备号操作
#include <linux/types.h>
#include <linux/kdev_t.h>
//device tree
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
//interrupt
#include <linux/interrupt.h>
#include <linux/gpio.h>
#include <linux/timer.h>
// queue 
#include <linux/workqueue.h>
//misc
#include <linux/miscdevice.h>
// chardev device class
#include <linux/cdev.h>	
#include <linux/device.h> 
// 向应用层通知信息
#include <asm/siginfo.h>
#include <linux/pid.h>
#include <linux/uaccess.h>
#include <linux/sched/signal.h>
// 驱动头文件
#include "driver_include.h"

struct driver_class driver_class_dev;

// 这是通过insmod装载时传递的两个参数为主设备号和次设备号 例如：insmod chardev_driver major_num = 200, minor_num = 0
static int major_num, minor_num; //定义主设备号和次设备号
module_param(major_num, int, S_IRUSR); //驱动模块传入普通参数 major_num
module_param(minor_num, int, S_IRUSR); //驱动模块传入普通参数 minor_nu
/*******************************************chardev_fops******************************************************/
/*
 * @description     : 打开设备
 * @param - inode   : 传递给驱动的inode
 * @param - filp    : 设备文件，file结构体有个叫做private_data的成员变量
 *                    一般在open的时候将private_data指向设备结构体。
 * @return          : 0 成功;其他 失败
 */
static int chardev_open (struct inode *inode, struct file *file)
{
    printk(KERN_EMERG "[kernle] hello chardev_open!\n");
    return 0;
}

/*
 * @description     : 关闭/释放设备
 * @param - inode   : 传递给驱动的inode
 * @param - filp    : 要关闭的设备文件(文件描述符)
 * @return          : 0 成功; 其他 失败
 */
static int chardev_release(struct inode *inode, struct file *file)
{
    printk(KERN_EMERG "[kernle] bye bye chardev_release!\n");
    return 0;
}

/*
 * @description     : 从设备读取数据 
 * @param - file    : 要打开的设备文件(文件描述符)
 * @param - ubuf    : 返回给用户空间的数据缓冲区
 * @param - cnt     : 要读取的数据长度
 * @param - offt    : 相对于文件首地址的偏移
 * @return          : 读取的字节数，如果为负值，表示读取失败
 */
static ssize_t chardev_read(struct file *file, char __user *ubuf, size_t cnt, loff_t *offt)
{
    int ret;
    ret = copy_to_user(ubuf, &driver_class_dev.app_pid, sizeof(driver_class_dev.app_pid));
    return ret;
}

/*
 * @description     : 从设备节点写入数据 
 * @param - file    : 要打开的设备文件(文件描述符)
 * @param - ubuf    : 返回给用户空间的数据缓冲区
 * @param - cnt     : 要写入的数据长度
 * @param - offt    : 相对于文件首地址的偏移
 * @return          : 写入的字节数，如果为负值，表示读取失败
 */
static ssize_t chardev_write(struct file *file, const char __user *ubuf, size_t cnt, loff_t *offt)
{
    char kbuf[64];
    if( copy_from_user(kbuf, ubuf, cnt) != 0 ){
        printk(KERN_EMERG "[kernle] copy_from_user error!\n");
        return -1;
    }
    printk("[kernle] buf is:%s", kbuf);
    return 0;
}

/*
 * @description     : 对设备I/O通道进行管理的函数
 * @param - file    : 要打开的设备文件(文件描述符)
 * @param - cmd     : 应用层传入的指令
 * @param - arg     : 应用层传入的数据
 * @return          : 0 成功; 其他 失败
 */
static long chardev_ioctl(struct file* file, unsigned int cmd, unsigned long arg)
{
    void __user *pArg;
	pArg = (void *)arg;

    switch(cmd)
    {
        case IOCTL_START:
            break;
        case IOCTL_SET_PID:
        {
            // 检测参数是否正确
            if (!access_ok(int, pArg, sizeof(int)))
            {
                printk("[kernel] access failed!\n");
                return -EACCES;
            }
            // 把用户空间的数据复制到内核空间
            if (copy_from_user(&driver_class_dev.app_pid, pArg, sizeof(int)))
            {
                printk("[kernel] copy_from_user failed!\n");
                return -EFAULT;
            }
            printk("[kernel] copy_from_user pid:%d!\n", driver_class_dev.app_pid);
            break;
        }
        case IOCTL_END:
            break;
        default:break;
    }
    return 0;
}

/* 操作函数结构体 */
struct file_operations chardev_fops = {
    .owner          = THIS_MODULE,
    .open           = chardev_open,
    .release        = chardev_release,
    .read           = chardev_read,
    .write          = chardev_write,
    .unlocked_ioctl = chardev_ioctl,
};
/*******************************************chardev_fops end**************************************************/

/**********************************************kernel to app**************************************************/
// 用来发送信号给应用程序
static void kernel_to_app_signal(int pid, struct siginfo s_info)
{
	int ret;
	struct task_struct *to_app_task = NULL;
	if (0 == pid)
	{
		// 说明应用程序没有设置自己的 PID
	    printk("[kernel] pid[%d] is not valid!\n", pid);
	    return;
	}

	// 获取自己的任务信息，使用的是 RCU 锁
	rcu_read_lock();
	to_app_task = pid_task(find_vpid(pid), PIDTYPE_PID);
	rcu_read_unlock();
	if (to_app_task == NULL)
	{
	    printk("[kernle] get pid_task failed!\n");
	    return;
	}
	// 发送信号
	ret = send_sig_info(s_info.si_signo, (struct siginfo *)&s_info, to_app_task);
	if (ret < 0) 
	{
	       printk("[kernle] send signal failed!\n");
	}
}
/******************************************kernel to app end**************************************************/

/************************************************handle*******************************************************/
// top half 处理紧急的事情
static irqreturn_t irq_top_handle(int irq, void *args)
{
    // 中断下文tasklet调度
    // tasklet_schedule(&driver_class_dev.tasklet);

    // 延时工作队列调度
    sprintf(driver_class_dev.work.name, "hello work");
    // queue_delayed_work(driver_class_dev.work_queue, &driver_class_dev.work.delay_work, 3 * HZ);  // HZ表示时钟节拍在内核中配置，一般都是100 表示一秒跳动100次 这里表示3秒
    queue_delayed_work(driver_class_dev.work_queue, &driver_class_dev.work.delay_work, 10); // 100ms

    // 普通工作队列调度
    // sprintf(driver_class_dev.work.name, "hello work");
    // queue_work(driver_class_dev.work_queue, &driver_class_dev.work.none_work); 

    // printk("interrupt_irq_top_handle\n");

    return IRQ_RETVAL(IRQ_HANDLED);
}

// down half 处理不紧急或者耗时的事情,禁止使用休眠函数sleep否则系统将会崩溃
void tasklet_down_handle(unsigned long data)
{
    printk("[kernle] interrupt_irq_tasklet_down_handle data:%ld\n", data);   
}

// down half 工作项处理函数 可以处理更加不紧急的事情且可以保持休眠使用sleep
void work_queue_handle(struct work_struct *work)
{
    struct siginfo irq_info;

    // 延时工作队列获取参数的方法：先找到延时工作项的结构体再去找work_data
    struct delayed_work *tmp_delay_work;
    struct work_data *temp_work;
    tmp_delay_work = container_of(work, struct delayed_work, work);
    temp_work = container_of(tmp_delay_work, struct work_data, delay_work);

    // 普通工作队列获取参数的方法
    // struct work_data *tmp_work;
    // tmp_work = container_of(work, struct work_data, none_work);

    // msleep(50); 
    // 构造信号结构体
	memset(&irq_info, 0, sizeof(struct siginfo));
	irq_info.si_signo = SIGIO;
	irq_info.si_errno = 0;
	irq_info.si_code  = 1;
    kernel_to_app_signal(driver_class_dev.app_pid, irq_info);

    printk("[kernle] work_queue_handle work.name = %s\n", temp_work->name);     
}

// 定时功能函数
static void timer_handle(unsigned long data)
{
    static int count = 0;
    printk("[kernle] timer handle event! data = %ld, count = %d\n", data, count++);
   
    if(count < 10)
    {
        //使用 mod_timer 函数将定时时间设置为1秒执行一次
        mod_timer(&driver_class_dev.timer, jiffies_64 + msecs_to_jiffies(1000));
    }
}
/**********************************************handle end****************************************************/


/********************************************register func***************************************************/
/* 字符设备注册函数 
 * 成功返回0 失败返回-1
 */
static int register_chrdev_func(void)
{
    int ret;
    // 字符设备注册
	if(major_num != 0) 
	{
        driver_class_dev.major_num = major_num;
        driver_class_dev.minor_num = minor_num;
        driver_class_dev.dev_num = MKDEV(major_num, minor_num);
        // 静态注册设备号
		printk("[kernle] major_num = %d minor_num = %d\n", major_num, minor_num); //打印传入进来的设备号
		ret = register_chrdev_region(driver_class_dev.dev_num, DEVICE_NUMBER, CHARDEV_NAME); //注册设备号
		if (ret < 0)
		{
			printk(KERN_ALERT "[kernle] register_chrdev_region error\n");
			return -1;
		}
	}else 
	{
        // 动态注册设备号
		ret = alloc_chrdev_region(&driver_class_dev.dev_num, DEVICE_MINOR_NUMBER, DEVICE_NUMBER, CHARDEV_NAME);
		if (ret < 0)
		{
			printk(KERN_ALERT "[kernle] Failed to allocate character device region\n");
			return -1;
		}
		
		driver_class_dev.major_num = MAJOR(driver_class_dev.dev_num);
		driver_class_dev.minor_num = MINOR(driver_class_dev.dev_num);
		
		printk("[kernle] major_num = %d minor_num = %d\n",driver_class_dev.major_num, driver_class_dev.minor_num); //打印申请来的设备号
	}
    
	// 字符设备的注册
    driver_class_dev.chrdev.owner = THIS_MODULE;
    cdev_init(&driver_class_dev.chrdev, &chardev_fops); // 函数初始化 cdev 结构体成员变量
	ret = cdev_add(&driver_class_dev.chrdev, driver_class_dev.dev_num, DEVICE_NUMBER); // 添加字符设备到内核
    if (ret < 0)
    {
        unregister_chrdev_region(driver_class_dev.dev_num, DEVICE_NUMBER);
        printk(KERN_ALERT "[kernle] Failed to add character device\n");
        return -1;
    }
    return 0;
}

/* 创建class和device用来创建设备节点 
 * 成功返回0 失败返回-1
 */
static int creat_class_and_device_func(void)
{
    // 自动创建设备节点
	driver_class_dev.cls = class_create(THIS_MODULE, DEVICE_CLASS_NAME);
	// 在 class 类下创建设备
	driver_class_dev.dev = device_create(driver_class_dev.cls, NULL, driver_class_dev.dev_num, NULL, DEVICE_NODE_NAME);

    return 0;
}

// 中断申请与中断上下文绑定
int creat_interrup_func(int irq)
{
    int ret;

    driver_class_dev.irq_num = irq;

    // 申请中断
    ret = request_irq(driver_class_dev.irq_num, irq_top_handle, IRQF_TRIGGER_FALLING, INTERRUPT_NAME, NULL);
    if(ret < 0)
    {
        printk(KERN_INFO "[kernle] Request IRQ = %d eRROR!\n", irq); 
        return -1;
    }

    // tasklet
    tasklet_init(&driver_class_dev.tasklet, tasklet_down_handle, 1);// data的值这里传递一个1

    // 工作队列
    driver_class_dev.work_queue = create_workqueue(WORKQUEUE_NAME);
    // driver_class_dev.work_queue = alloc_workqueue(WORKQUEUE_NAME, WQ_UNBOUND, 0); // 并发

    // 普通工作项
    // INIT_WORK(&driver_class_dev.work.none_work, work_queue_handle);   

    // 延时工作项
    INIT_DELAYED_WORK(&driver_class_dev.work.delay_work, work_queue_handle); // 初始化延时工作项

    return 0;
}
/******************************************register func end**************************************************/

/*********************************************chrdev probe****************************************************/
static int template_platform_probe(struct platform_device *pdev)
{
	int ret;
    struct device *dev = &pdev->dev;
#if 0
	struct property *compatible_property; // compatible属性
	u32 regdata[10]; // reg数组
	
    // 设备数属性获取
	driver_class_dev.devicetree_node = of_find_node_by_path("/devicetree_platform"); // 需要注意"devicetree_platform"的节点必须写在根节点也就是需要对应路径
	if(driver_class_dev.devicetree_node == NULL) {
		printk("[kernle] driver_class_dev.devicetree_node can not found!\r\n");
		return -EINVAL;
	} else {
		printk("[kernle] driver_class_dev.devicetree_node has been found!\r\n");
	}
	/* 获取 compatible 属性内容 */
	compatible_property = of_find_property(driver_class_dev.devicetree_node, "compatible", NULL);
	if(compatible_property == NULL) {
		printk("[kernle] compatible property find failed\r\n");
	} else {
		printk("[kernle] compatible = %s\r\n", (char*)compatible_property->value);
	}
	
	/* 获取 reg 属性内容 reg[0] = 0x020c406c reg[1] = 0x04 ....*/
	ret = of_property_read_u32_array(driver_class_dev.devicetree_node, "reg", regdata, 10);
	if(ret < 0) {
		printk("[kernle] reg property read failed!\r\n");
	} else {
		u8 i = 0;
		printk("[kernle] reg data:\r\n");
		for(i = 0; i < 10; i++)
		printk("%#X ", regdata[i]);
		printk("\r\n");
	}
#endif

    // 字符设备注册
    if(register_chrdev_func() < 0)
    {
        return -1;
    }
    // 设备节点创建
	if(creat_class_and_device_func() < 0)
    {
       return -1; 
    }
    // 中断创建
    // ret = creat_interrup_func(gpio_to_irq(1 * 32 + 5)) ; // PB5 = 1 * 32 + 5
    ret = creat_interrup_func(gpio_to_irq(of_get_named_gpio(dev->of_node,"gpios", 0)));
    if(ret < 0)
    {
        return -1;
    }
    // 定时器初始化
    init_timer(&driver_class_dev.timer);
    driver_class_dev.timer.data = 123;
    driver_class_dev.timer.function = timer_handle;
    driver_class_dev.timer.expires = jiffies_64 + msecs_to_jiffies(1000); // 定时1000ms
    // add_timer(&driver_class_dev.timer); // 启动定时器

    printk(KERN_INFO "[kernle] Character device registered: %s DEVICE_NODE_NAME = %s\n", CHARDEV_NAME, DEVICE_NODE_NAME);
	return 0;
}

// template移除信号函数，释放相关设备
static int template_platform_remove(struct platform_device *pdev)
{
    cdev_del(&driver_class_dev.chrdev);// 注销字符设备

    device_destroy(driver_class_dev.cls, driver_class_dev.dev_num);//注销设备
    class_destroy(driver_class_dev.cls);// 注销类

    free_irq(driver_class_dev.irq_num, NULL); // 中断号释放

    // 中断下文关闭并删除
	tasklet_enable(&driver_class_dev.tasklet); 
	tasklet_kill(&driver_class_dev.tasklet);

    // 工作队列释放
    // cancel_work_sync(&driver_class_dev.work.none_work); // 取消工作项
    cancel_delayed_work_sync(&driver_class_dev.work.delay_work); // 取消延时工作项
    flush_workqueue(driver_class_dev.work_queue);   // 刷新工作队列
    destroy_workqueue(driver_class_dev.work_queue); // 销毁工作队列

    del_timer(&driver_class_dev.timer);//删除定时器

	printk("[kernle] %s %d\n", __FUNCTION__, __LINE__);
	return 0;
}

// 设备树 匹配属性 优先级1
static const struct of_device_id template_of_match[] = {
	{ .compatible = Of_MATCH_COMPATILBLE, }, 
};

// platform device 匹配id 优先级2
static const struct platform_device_id template_id_table = {
	.name = ID_TABLE_COMPATILBLE
};

// platform总线结构体
static struct platform_driver template_platform_driver = {
	.probe		= template_platform_probe,
	.remove		= template_platform_remove,
	.driver		= {
		.name	= "template_platform",  // 优先级3
		.of_match_table = of_match_ptr(template_of_match),
	},
	.id_table 	= &template_id_table	// 该节点匹配不上时使用name属性进行匹配 优先级2
};

/*******************************************chrdev probe end**************************************************/

/*******************************************module init exit**************************************************/
/*
 * @description : 驱动入口函数
 * @param       : 无
 * @return      : 无
 */
static int __init template_driver_init(void)
{
    int ret;
	/* 注册一个platform_driver */
    ret = platform_driver_register(&template_platform_driver);
    printk("[kernle] %s %d\n", __FUNCTION__, __LINE__);
	return ret;
}

/*
 * @description : 驱动出口函数
 * @param       : 无
 * @return      : 无
 */
static void __exit template_driver_exit(void)
{
	/* 反注册platform_driver */
	platform_driver_unregister(&template_platform_driver);
    printk("[kernle] %s %d\n", __FUNCTION__, __LINE__);
}

module_init(template_driver_init);
module_exit(template_driver_exit);

/*******************************************module init exit end**********************************************/

MODULE_AUTHOR("Kongjun"); // 作者
MODULE_DESCRIPTION("template driver"); // 驱动介绍可以不写
MODULE_LICENSE("GPL");// 开源协议
